#pragma once


class SpriteDef
{
public:
	static	std::map<std::string,Texture*>			textureCache;
	static	std::map<std::string,Model*>			modelCache;
	static	std::vector<SpriteDef*>					spriteDefs;
	static Model*	findModel(const char* nm){
		std::map<std::string,Model*>::iterator fnd=modelCache.find(nm);
		if(fnd!=modelCache.end())return fnd->second;
		return null;
	}	
	static Model*	getModel(const char* baseName);

	class	ListenerPool{
	public:
		std::vector<Listener*>	pool;
		int		top;
		int		size;
		bool	active;
		void	clear(){
			top=0;
			active=false;
		}
		bool	sendMessage(const char*	msg=null,void* params=null){
			for(int i=0;i<top;i++){
				if(!pool[i]->message(msg,params))
					return false;
			}
			return true;
		}
		void	push(Listener*	l){
			if(top>=size){
				pool.push_back(l);
				top++;
				size++;
			}else
				pool[top++]=l;
			if(top==1){//First push.. flag the queue as active
				if(activePoolTop==activePools.size()){
					activePools.push_back(this);
					activePoolTop++;
				}else
					activePools[activePoolTop++]=this;
			}
		}
		ListenerPool(){
			active=false;
			top=size=0;
		}
	};
	static	std::map<Model*,ListenerPool>	drawPools;
	static	std::vector<ListenerPool*>		activePools;
	static	unsigned int					activePoolTop;
	std::vector<Model*>		models;
	std::vector<TextureID>	textures;
	std::vector<ShaderID>	shaders;
	static void	resetDrawPools();
	static void	renderDrawPools();
	void	addToDrawPool(Listener*	listener);
	void	prepModel(Renderer*	renderer,Model*	model,const Light *lights,int	lightIndex);

	void	draw(Renderer*	renderer,const Light *lights,int	lightIndex);
};

struct SpriteFlags
{
	unsigned rebuildMatrix	 : 1;
	unsigned isMoving		 : 1;
	unsigned isSelected		 : 1;
};

extern SpriteFlags	nullFlags;

class bSprite : public Listener{
public:
	SpriteFlags	flags;
	static	growvector<bSprite*> displayList;
	static	growvector<bSprite*> activeSprites;
	virtual bool	message(const char *msg=null,void*	params=null){return true;};

	bSprite(){
		activeSprites.push_back(this);
		flags=nullFlags;
	}
	static	void	renderDisplayList(){
		for(unsigned int t=0;t<displayList.size();t++){
			displayList[t]->message("render");
		}
		displayList.clear();
	}
	static	void	updateAll(){
		for(unsigned int t=0;t<activeSprites.size();t++){
			activeSprites[t]->message("update");
		}
	}
	static	void	renderAll(){
//		displayTop=0;
		for(unsigned int t=0;t<activeSprites.size();t++){
			displayList.push_back(activeSprites[t]);
		}
		renderDisplayList();
	}
	virtual ~bSprite(){};
};

class Sprite;


class SpriteController{
public:
	virtual void	update(Sprite*	spr){};
	virtual void	render(Sprite*	spr){};
	SpriteController(){};
	~SpriteController(){};
};

class Sprite : public bSprite{
public:
	static		int					spriteBaseID;
	static	std::map<int,Sprite*>	idMap;
	static	growvector<Sprite*>		selectableStack;
	quaternion	quat;
	vec3		position;
	mat4		transform;
	SpriteDef*			definition;
	SpriteController*	controller;
	int			id;

	static Sprite*	findSprite(int id){
		std::map<int,Sprite*>::iterator it=idMap.find(id);
		if(it==idMap.end())return null;
		return it->second;
	}
	virtual	float	getRadius(){
		return 1.0f;
	}
	static void	resetSelection(){
		selectableStack.clear();
	}
	virtual bool	allowRaycastGeometry(){
		return false;
	}
	virtual bool	message(const char *msg=null,void*	params=null);
	Sprite();
};




class SpriteDrawPool{
public:
	std::vector<bSprite*>	visibleStack;
	unsigned int			visibleTop;
	void	push(bSprite* spr){
		if(visibleTop==visibleStack.size())	visibleStack.push_back(spr);
		else visibleStack[visibleTop]=spr;
		visibleTop++;
	}
	SpriteDrawPool(){visibleTop=0;}
	void	reset(){visibleTop=0;}
};

class LocalGeometryCache{
public:
	static std::vector<vec3>	vertexBuffer;	
	static std::vector<vec2>	uvBuffer;
	static void	renderVisible();
	LocalGeometryCache();
	virtual ~LocalGeometryCache(){};
};


class SpriteOverlay:	public bSprite{
public:
	static ShaderID			shader;
	static SpriteDrawPool	drawPool;
	vec3					position;
	void				markForRendering(){
		drawPool.push(this);
	}
	static	void			resetDrawPools(){
		drawPool.reset();
	}
	static	void			renderVisible();
	SpriteOverlay();

	vec4				color;
	std::string			text;
	void	update(vec3	pos);
	void render(vec3 &dx, vec3 &dy);
};

class SpriteTrail:	public bSprite{
public:
	static SpriteDrawPool	drawPool;
	void				markForRendering(){
		drawPool.push(this);
	}
	static	void			resetDrawPools(){
		drawPool.reset();
	}
	static	void			renderVisible();
	void render(vec3 &dx, vec3 &dy);
	

	SpriteTrail();
	float				trailSize;
	std::vector<vec3>	buffer;
	float				stretchLength;
	vec4				color;
	void	update(vec3	pos);

};

bool	loadSpriteDef(char*	baseName,char* shader,SpriteDef*	ndef);